#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using namespace LogMoudle;
using namespace ThraedPoolMoudule;
using namespace ThreadModlue;

// using task_t = std::function<void()>; // 定义任务类型参数为空，返回值为void
using func_t = std::function<std::string(const std::string&, InetAddr &)>;

const static int defaultsockfd = -1;
const static int backlog = 8; // 底层权限队列长度

// 服务器禁止拷贝，继承NoCopy(拷贝构造和赋值语句都被delete，基类被禁止拷贝)
class TcpServer : public ::NoCopy
{
public:
    TcpServer(uint16_t port, func_t func)
        : _port(port), 
         _listensockfd(defaultsockfd), 
         _isrunning(false), 
         _func(func)
    {
    }
    void Init()
    {
        // signal(SIGCHLD, SIG_IGN); // 忽略SIGCHLD信号
        // 1. 创建TCP套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3

        // 2. bind众所周知的端口号
        InetAddr local(_port);
        int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind eror";
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";

        // 3.设置监听
        n = listen(_listensockfd, backlog);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen eror";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success";
    }

    class ThreadDate
    {
    public:
        ThreadDate(int fd, InetAddr addr, TcpServer *tsvr) : sockfd(fd), addr(addr), tsvr(tsvr)
        {
        }

    public:
        int sockfd;
        InetAddr addr;
        TcpServer *tsvr;
    };

    // 长服务：多进程多线程比较合适
    void Service(int sockfd, InetAddr &peer)
    {
        char buffer[1024];
        while (true)
        {
            // 1. 读取数据
            // n > 0 读取成功
            // n < 0 读取失败
            // n = 0 对端把链接关闭了，读到了文件的结尾
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0; // 设置为字符串, 一般返回值 <= sizeof(buffer)-1
                LOG(LogLevel::DEBUG) << peer.StringAddr() << " say# " << buffer;
                std::string echo_string = _func(buffer, peer); // 回调，在上层处理，把buffer当做字符串 or 当做命令字符串

                // // 2. 写回数据
                // std::string echo_string = "echo# ";
                // echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";
                close(sockfd);
                break;
            }
            else
            {
                LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常了...";
                close(sockfd);
                break;
            }
        }
    }

    // 类内方法，要用static修饰，但是静态函数又不能访问类内成员函数，所以直接在ThreadData类内定义一个指针
    static void *Routine(void *args)
    {
        pthread_detach(pthread_self()); // 分离当前线程
        ThreadDate *td = static_cast<ThreadDate *>(args);
        td->tsvr->Service(td->sockfd, td->addr);
        delete td; // 记得
        return nullptr;
    }

    void Run()
    {
        _isrunning = true;

        while (_isrunning)
        {
            // 1. 先获取新链接
            struct sockaddr_in peer;
            socklen_t len = sizeof(sockaddr_in);
            int sockfd = accept(_listensockfd, CONV(peer), &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error";
                continue;
            }
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "accept success, peer addr: " << addr.StringAddr();

            // version 2 --- 多线程版本
            ThreadDate *td = new ThreadDate(sockfd, addr, this);

            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);

            //-----------------------------------------------------

            // version 0 测试单进程程序(BUG)
            // Service(sockfd, addr);

            //-----------------------------------------------------

            // version 1 --- 多进程版本
            // pid_t id = fork();
            // if(id < 0)
            // {
            //     LOG(LogLevel::FATAL) << "fork error";
            //     exit(FORK_ERR);
            // }
            // else if(id == 0)
            // {
            //     // child 可以看到father的listensockfd, 要关闭
            //     close(_listensockfd);

            //     if(fork() > 0)  // 再次fork后, 新子进程的父进程退出
            //         exit(OK);

            //     Service(sockfd, addr); // 孙子进程---业务处理
            //     exit(OK);
            // }
            // else
            // {
            //     // father
            //     close(sockfd);

            //     // 等待子进程时时阻塞的！
            //     // 方法1：使用signal(SIGCHLD, SIG_IGN),忽略SIGCHLD 信号后,
            //     // wait()不会阻塞，系统会自动回收已忽略 SIGCHLD 信号的子进程
            //     // 方法2：在子进程的位置再次fork, 让孙子进程进行业务处理。此处waitpid就不会阻塞, 因为子进程已经退出了
            //     pid_t rid = waitpid(id, nullptr, 0);
            //     (void)rid;
            // }

            //-----------------------------------------------------

            // // version 2 --- 多线程版本
            // ThreadDate *td = new ThreadDate(sockfd, addr, this);

            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            //-----------------------------------------------------

            // // version 3 --- 线程池版本，适合处理短服务
            // // 将新链接和客户端构建一个新任务(单例)，push到线程池中
            // ThreadPool<task_t>::GetInstance()->Enqueue([this, sockfd, &addr](){
            //     this->Service(sockfd, addr);
            // });

            //-----------------------------------------------------
        }
        _isrunning = false;
    }

    ~TcpServer() {}

private:
    uint16_t _port;
    int _listensockfd; // 监听套接字

    bool _isrunning;
    func_t _func; // 设置回调处理
};
